'''
Connect 4 game management code
Steven Bogaerts

WARNING: Do not change anything in this file!  When I grade and when I run
the tournament, I will use a fresh copy of this file, so any changes you make
to a personal copy will not be included.

If you want to make        if isTerminalState(state):
            if playerToMove == self.playerSymbol:
                return 100000

        value = 0

        #check horizontal

        for col in range(4):
            row = getLowestRowInCol(getColumn(state, col))
            if type(row) == type(1):
                if (isNSeqHoriz(4, state, col, row) == self.playerSymbol):
                    value += 10**4
                elif (isNSeqHoriz(3, state, col, row) == self.playerSymbol):
                    value += 10**3
                elif (isNSeqHoriz(2, state, col, row) == self.playerSymbol):
                    value += 10**2

        col = 4
        row = getLowestRowInCol(getColumn(state, col))
        if type(row) == type(1):
            if (isNSeqHoriz(3, state, col, row) == self.playerSymbol):
                value += 10**3
            elif (isNSeqHoriz(2, state, col, row) == self.playerSymbol):
                value += 10**2

        col = 5
        row = getLowestRowInCol(getColumn(state, col))
        if type(row) == type(1):
            if (isNSeqHoriz(2, state, col, row) == self.playerSymbol):
                value += 10**2 a change to something here, you should copy the code into
your own file and change that.  I can see that potentially being useful
depending on what you're wanting to do, so feel free to talk with me about
this.

With the exception of the player definitions, this uses imperative, rather than
object-oriented, programming.  This is to facilitate translation into C if
desired (for faster execution).

You can play Connect 4 online here:
http://www.mathsisfun.com/games/connect4.html
'''

# --------------------------------------------------------------------------------------------
# Top-level functions

'''
To play a game in various ways:
 startConnect4(HumanDef, HumanDef, 200)
 startConnect4(RandomDef, HumanDef, 200)
 startConnect4(HumanDef, RandomDef, 200)
 startConnect4(RandomDef, RandomDef, 200)
Or you can create your own player definition to use - one that uses minimax,
for example.

You can also specify -1 for times, if you don't want timing to be a factor in
the game.  For example:
 startConnect4(HumanDef, HumanDef, -1)
'''

NUM_COLS = 7
NUM_ROWS = 6

'''
Sets things in motion by calling playGame with the proper arguments

totalTime is the time in seconds allowed for one player.  (So each player gets
totalTime seconds across the entire game.)  It's like a chess clock.  Each
time your program is asked for a move, the remaining time is passed as a
parameter. Suppose you're playing a game with totalTime = 128 per player,
your program has already used 20 seconds (timeLeft = 108), it is asked for
a move, and takes 10 seconds to respond. Then the next time your program is
asked for a move, timeLeft would be 98. If your program then used, say, 100
seconds for its next move, then it would forfeit the game.
'''
def startConnect4(playerXClass, playerOClass, totalTime):
    playGame(totalTime, makeInitState(), 'x',
             playerXClass('x', totalTime), playerOClass('o', totalTime),
             totalTime, totalTime)

'''
Manages one entire game, calling the appropriate player definition for a move,
handling display of information, timing, etc.
currPlayerSymbol is 'x' or 'o'.
The game always starts with player 'x'.
'''
from time import time # Time is in seconds in Python
#from tNA3 import *
from copeFinkA3 import *


def playGame(totalTime, state, currPlayerSymbol,
             playerXDef, playerODef,
             currPlayerTimeLeft, otherPlayerTimeLeft):
    gameOver = False
    while not gameOver:
        # ------------------------------------------------------
        # Display game information
        printState(state)
        if (totalTime != -1):
            print "%s, %s seconds left." % (currPlayerSymbol, currPlayerTimeLeft)
        else:
            print "%s, your turn." % currPlayerSymbol

        # ------------------------------------------------------
        # Get player move

        # Copy the state, so the playerDef getMove function can change it if it wants
        copiedState = stateCopy(state)

        start = time()
        if currPlayerSymbol == 'x':
            playerMove = playerXDef.getMove(copiedState, currPlayerTimeLeft)
        else:
            playerMove = playerODef.getMove(copiedState, currPlayerTimeLeft)
        end = time()
        newCurrPlayerTimeLeft = currPlayerTimeLeft - (end-start)

        print "========================================="

        # ------------------------------------------------------
        # Check if game is over...
        if not isValidMove(state, playerMove): # ...by invalid move...
            print "Invalid move by computer player %s.  %s forfeits.\n" % (currPlayerSymbol, currPlayerSymbol)
            terminalResults = otherPlayer(currPlayerSymbol)
            gameOver = True
        elif newCurrPlayerTimeLeft < 0 and totalTime != -1: # ...by running out of time...
            print "Player %s ran out of time.  %s forfeits.\n" % (currPlayerSymbol, currPlayerSymbol)
            terminalResults = otherPlayer(currPlayerSymbol)
            gameOver = True
        else:
            # Make the move actually occur
            move(state, playerMove, currPlayerSymbol)

            # Check if game is over
            terminalResults = isTerminalState(state)
            if terminalResults:
                gameOver = True

            # Switch whose turn it is
            currPlayerSymbol = otherPlayer(currPlayerSymbol)
            currPlayerTimeLeft = otherPlayerTimeLeft
            otherPlayerTimeLeft = newCurrPlayerTimeLeft

    # Game's over now!
    showResults(state, terminalResults)

'''
Switch to the other player symbol (for alternating turns)
'''
def otherPlayer(player):
    if player == 'x':
        return 'o'
    else:
        return 'x'

'''
Show results of a complete game
Result is 'x', 'o', or 'c' (for tie game)
'''
def showResults(state, result):
    print
    printState(state)
    if result == 'c':
        print "Cat game!  No winner."
    else:
        print "%s wins!" % result
        
# -------------------------------------------------------------------------------
# State and related functions

'''
State representation is a list of lists.  The top-level list specifies the column,
and the bottom level is the row.

This might be backwards from what would typically be expected, but this is convenient
since often we are interested in working within a single column.  So it is better for
this single column to be in a single list, rather than spread across multiple lists.

For example, a state can be "pretty printed" using printState, and might look like
this, with column numbers written at the top:
0 1 2 3 4 5 6
_ _ _ _ _ _ _
_ _ _ _ _ _ _
_ _ _ _ _ _ _
_ _ _ _ x _ _
_ _ x o o _ _
x o o x x _ _

This is represented internally as:

   top ----------------> bottom
row 0    1    2    3    4    5
 [['_', '_', '_', '_', '_', 'x'],  # col 0
  ['_', '_', '_', '_', '_', 'o'],  # col 1
  ['_', '_', '_', '_', 'x', 'o'],  # col 2
  ['_', '_', '_', '_', 'o', 'x'],  # col 3
  ['_', '_', '_', 'x', 'o', 'x'],  # col 4
  ['_', '_', '_', '_', '_', '_'],  # col 5
  ['_', '_', '_', '_', '_', '_']]  # col 6

So to properly interpret the list of lists representation, you have to tilt your head
left and look at it in a mirror...

Here's another way to look at it.  Using the "pretty print" view of printState, here
are the coordinates for each position, in (col, row) format (*not* (row, col)):

(0,0) (1,0) (2,0) (3,0) (4,0) (5,0) (6,0)
(0,1) (1,1) (2,1) (3,1) (4,1) (5,1) (6,1)
(0,2) (1,2) (2,2) (3,2) (4,2) (5,2) (6,2)
(0,3) (1,3) (2,3) (3,3) (4,3) (5,3) (6,3)
(0,4) (1,4) (2,4) (3,4) (4,4) (5,4) (6,4)
(0,5) (1,5) (2,5) (3,5) (4,5) (5,5) (6,5)

Most of the time you won't need to worry about the internal representation - you can
just work with the accessor functions provided here and think about the "pretty
print" version that printState gives.
'''

'''
Make an empty state
'''
def makeInitState():
    cols = []
    for c in range(NUM_COLS):
        oneCol = []
        for r in range(NUM_ROWS):
            oneCol.append('_')
        cols.append(oneCol)
    return cols

'''
Get the list corresponding to a column with the given colID
'''
def getColumn(state, colID):
    return state[colID]

'''
Get the piece at the current position (might be 'x', 'o', or '_')
'''
def getPiece(state, colID, rowID):
    return getColumn(state, colID)[rowID]

'''
Neatly print out the board state
'''
def printState(state):
    # print column headings
    for i in range(NUM_COLS):
        print i,
    print

    for rowID in range(NUM_ROWS):
        for colID in range(NUM_COLS):
            print getPiece(state, colID, rowID),
        print

'''
Copy the state so it may be changed without messing up the original
'''
def stateCopy(state):
    newState = []
    for c in range(NUM_COLS):
        newState.append(state[c][:])
    return newState

'''
Changes (mutates) state to reflect the given move (indicated by colID, done by player
'x' or 'o').  If the requested column is full, the request is ignored.
'''
def move(state, colID, player):
    if not isFullCol(state, colID):
        boardCol = getColumn(state, colID)
        rowID = getLowestRowInCol(boardCol)
        boardCol[rowID] = player

'''
Returns row in which a move may be made for this column, or False if the column is
full.
'''
def getLowestRowInCol(boardCol):
    # Start at top row (rowID == 0), and go down until you find a piece
    for rowID in range(NUM_ROWS):
        piece = boardCol[rowID]
        if piece == 'x' or piece == 'o':
            if rowID == 0:
                return False # Column is full
            else:
                return rowID-1 # Just above current row is open
        # else still looking for top piece
    return rowID # No pieces in this column at all, so return NUM_ROWS-1
        
'''
Returns true if the move is valid (correct input type and not a full column).
'''
def isValidMove(state, colID):
    if type(colID) != type(1) or \
       colID < 0 or \
       colID > NUM_COLS - 1:
        return False
    else:
        return not isFullCol(state, colID)

'''
Returns true if the given column is full in the given state, false otherwise.
'''
def isFullCol(state, colID):
    return getColumn(state, colID)[0] != '_'

# -----------------------------------------------------------------------------------------
# isTerminalState and helpers

'''
To search for sequences of length n, there are four tests defined below:
h is isNSeqHoriz - sequence from current position, to the right
v is isNSeqVert - sequence from the current position, down
d is isNSeqDownRight - sequence from the current position, down and right
u is isNSeqUpRight - sequence from the current position, up and right

Using the printState() view of the state, with colID, rowID coordinates shown,
the four tests are applicable at the following locations for n=4:

00hvd   10hvd   20hvd   30hvd   40 v    50 v    60 v
01hvd   11hvd   21hvd   31hvd   41 v    51 v    61 v
02hvd   12hvd   22hvd   32hvd   42 v    52 v    62 v
03h  u  13h  u  23h  u  33h  u  43      53      63
04h  u  14h  u  24h  u  34h  u  44      54      64
05h  u  15h  u  25h  u  35h  u  45      55      65

In general, areas of effect (inclusive) for each test to find a sequence of length n:
   COL              ROW
h: 0..NUM_COLS-n    0..NUM_ROWS-1
v: 0..NUM_COLS-1    0..NUM_ROWS-n
u: 0..NUM_COLS-n    n-1..NUM_ROWS-1
d: 0..NUM_COLS-n    0..NUM_ROWS-n

These tests are immensely useful.  For isTerminalState, we set n=4 and look for a
sequence of 4.  (In a legal game, there will always be either 0 or 1 sequence of 4.)

These tests can also be helpful in developing an evalState function, since you might
be interested in finding sequences of length less than 4, or "windows" of length 4...
'''

'''
Returns 'x' if x won, 'o' if o won, False if no one has won, and 'c' for a "cat
game" (tie).
'''
def isTerminalState(state):
    foundBlank = False
    for rowID in range(NUM_ROWS):
        for colID in range(NUM_COLS):
            
            # Find tests that apply for the current position
            applicableSeqTests = getNSeqTests(4, state, colID, rowID)
            
            # Run the applicable tests
            seqTestResults = map(lambda testFunc: testFunc(4, state, colID, rowID),
                                 applicableSeqTests)
            
            # See if any test indicates a winner
            testResult = orList(seqTestResults)
            if testResult:
                return testResult # if found a terminal result, return it

            # Keep eyes peeled for a cat game (full state):
            # If already found a blank previously, or just did, make foundBlank True
            foundBlank = foundBlank or (getPiece(state, colID, rowID) == '_')

    # If we get here without already having returned, then there is no winner at
    # this point.
    if foundBlank:
        return False # Found a blank, so not terminal
    else:
        return 'c' # No blanks, and didn't find a winner - so cat game

'''
Does an "or" on a list of booleans, except that when it finds anything non-False, it
returns it, instead of returning True.
'''
def orList(boolList):
    for b in boolList:
        if b:
            return b
    return False

'''
The following four functions test for the area of effect of a sequence test,
as described above.
'''
def isInHBoundary(n, colID, rowID):
    return colID <= NUM_COLS - n and rowID <= NUM_ROWS - 1

def isInVBoundary(n, colID, rowID):
    return colID <= NUM_COLS - 1 and rowID <= NUM_ROWS - n

def isInUBoundary(n, colID, rowID):
    return colID <= NUM_COLS - n and rowID >= n - 1 and rowID <= NUM_ROWS - 1

def isInDBoundary(n, colID, rowID):
    return colID <= NUM_COLS - n and rowID <= NUM_ROWS - n

'''
Takes a sequence length n, the state, and a position.
Returns a list of the tests (among isNSeqVert, isNSeqHoriz, isNSeqDownRight,
isNSeqUpRight) that apply to that position.
'''
def getNSeqTests(n, state, colID, rowID):
    boundaryTests = [isInHBoundary, isInVBoundary, isInUBoundary, isInDBoundary]
    seqTests = [isNSeqHoriz, isNSeqVert, isNSeqUpRight, isNSeqDownRight]
    
    appSeqTests = [] # Will contain the list of sequence tests applicable at (colID, rowID)
    
    for pos in range(4):  # Because there are 4 tests
        bTestResult = boundaryTests[pos](n, colID, rowID) # Is this position within the boundary
                                                          # for the nSeqTest specified by pos?
        if bTestResult: # Boundary test says we're within boundaries for current seq test
            appSeqTests.append(seqTests[pos]) # so add seq test to list of tests to execute

    return appSeqTests

'''
For each of the following four methods:
Takes an n, state, colID, rowID,
and looks for a sequence of length n with all the same pieces (all x,
or all o) in the desired direction.
Returns 'x' if x has such a sequence, 'o' if o does, or False otherwise.
'''
def isNSeqVert(n, state, colID, rowID):
    return isGeneralNSeq(n, state, colID, rowID,
                        (lambda colID, val: colID),
                        (lambda rowID, val: rowID + val))

def isNSeqHoriz(n, state, colID, rowID):
    return isGeneralNSeq(n, state, colID, rowID,
                        (lambda colID, val: colID + val),
                        (lambda rowID, val: rowID))

def isNSeqDownRight(n, state, colID, rowID):
    return isGeneralNSeq(n, state, colID, rowID,
                        (lambda colID, val: colID + val),
                        (lambda rowID, val: rowID + val))

def isNSeqUpRight(n, state, colID, rowID):
    return isGeneralNSeq(n, state, colID, rowID,
                        (lambda colID, val: colID + val),
                        (lambda rowID, val: rowID - val))

'''
Takes length of sequence n, state, colID and rowID for start of possible sequence,
and functions defining how to advance in the desired direction.
Returns 'x' if x has such a sequence, 'o' if o does, or False otherwise.
'''
def isGeneralNSeq(n, state, colID, rowID, colOp, rowOp):
    basePiece = getPiece(state, colID, rowID)
    
    if basePiece == '_':  # if blank, then no sequence starts here
        return False
    else:
        for val in range(1, n):
            if basePiece != getPiece(state, colOp(colID, val), rowOp(rowID, val)):
                return False # current piece didn't match, so no n-seq here
        return basePiece # If you got this far, then every test was true,
                         # so there is an n-seq for basePiece in this direction

# -------------------------------------------------------------------------------------------
# Debugging functions

'''
Here are a few slightly more complex functions I used for debugging, that might
come in handy again.
'''

'''
Make a state, given a history of moves.
X always moves first.  History is a list of columns in which moves were made.
'''
def makeState(history):
    players = ['x', 'o']
    state = makeInitState()
    playerID = 0
    for aMove in history:
        move(state, aMove, players[playerID])
        playerID = (playerID + 1) % 2
    return state

def testGetNSeqTests():
    state = makeInitState()
    for rowID in range(NUM_ROWS):
        for colID in range(NUM_COLS):
            tests = getNSeqTests(4, state, colID, rowID)
            result = ""
            for t in tests:
                if t == isNSeqHoriz:
                    result += 'h'
                elif t == isNSeqVert:
                    result += 'v'
                elif t == isNSeqUpRight:
                    result += 'u'
                elif t == isNSeqDownRight:
                    result += 'd'
            for i in range(4-len(tests)):
                result += '_'
            print result + " ",
        print

def testIsGeneralNSeq():
    # vertNSeq at col 6, row 2
    # horizNSeq at col 3, row 5
    # upRightNSeq at col 3, row 5
    # downRightNSeq at col 3, row 2
                # top -> bottom
    testState = [['_', '_', '_', '_', '_', '_'], # col 0
                 ['_', '_', '_', '_', '_', '_'],
                 ['_', '_', '_', '_', '_', '_'], 
                 ['_', '_', 'x', 'o', 'o', 'x'], 
                 ['_', '_', '_', 'x', 'x', 'x'],
                 ['_', '_', '_', 'x', 'x', 'x'],
                 ['_', '_', 'x', 'x', 'x', 'x']] # col 7
    printState(testState)
    print isNSeqVert(4, testState, 6, 2)
    print isNSeqHoriz(4, testState, 3, 5)
    print isNSeqDownRight(4, testState, 3, 2)
    print isNSeqUpRight(4, testState, 3, 5)

def testCatGame():
    testState = [['x', 'x', 'x', 'o', 'o', 'o'], # col 0
                 ['o', 'o', 'o', 'x', 'x', 'x'],
                 ['x', 'x', 'x', 'o', 'o', 'o'], 
                 ['o', 'o', 'o', 'x', 'x', 'x'],
                 ['x', 'x', 'x', 'o', 'o', 'o'], 
                 ['o', 'o', 'o', 'x', 'x', 'x'],
                 ['x', 'x', 'x', 'o', 'o', 'o']] # col 7
    print isTerminalState(testState)


##########################################################################################
# Player definitions

'''
Any player definition should inherit from this class, and define the
getMove method.
'''
class PlayerDef(object):
    def __init__(self, playerSymbol, totalTime):
        '''
        playerSymbol is either 'x' or 'o', indicating which symbol belongs to
        this player.
        totalTime is the total amount of time the program will have to play
        the game.
        '''
        self.playerSymbol = playerSymbol
        self.totalTime = totalTime
    
    def getMove(self, state, timeLeft):
        pass

# ----------------------------------------------------------------------------------------
# Simple player definitions

'''
This is an "abstract" class that should not be instantiated.
'''
class GeneralTestDef(PlayerDef):
    def __init__(self, playerSymbol, totalTime):
        super(GeneralTestDef, self).__init__(playerSymbol, totalTime)

    # Keeps asking test definition for a move until a correct one is obtained.
    def getMove(self, state, timeLeft):
        theMove = self._getMoveCol()
        while not isValidMove(state, theMove):
            self._invalidMoveResponse()
            theMove = self._getMoveCol()
        return theMove

'''
HumanDef asks for the move to be input from the keyboard
'''
class HumanDef(GeneralTestDef):
    def __init__(self, playerSymbol, totalTime):
        super(HumanDef, self).__init__(playerSymbol, totalTime)

    def _getMoveCol(self):
        return input("Enter move: ")

    def _invalidMoveResponse(self):
        print "Invalid move, human.  Try again: "

'''
RandomDef randomly (stupidly) selects a move until a valid one is found
'''
from random import randrange
class RandomDef(GeneralTestDef):
    def __init__(self, playerSymbol, totalTime):
        super(RandomDef, self).__init__(playerSymbol, totalTime)

    def _getMoveCol(self):
        return randrange(NUM_COLS) # get random move - will keep trying until valid
    
    def _invalidMoveResponse(self):
        pass # No response to invalid move - just try again


##################################################################
#
##################################################################

#constants
INITIAL_DEPTH = 5
EXTENDED_DEPTH = 3
MAX_PLAYER = True

class ParallelDef(GeneralTestDef):
    
    def __init__(self, playerSymbol, totalTime):
        super(ParallelDef, self).__init__(playerSymbol, totalTime)
        self.masterChief = MasterChief()

    def _getMoveCol(self):                                                                           ######TODO:  override with algorithm decision
        return input("Enter move: ")

    def moveItmoveIt(self, gameBoard):
        #var bestMoveColumn, maxValue, possMaxValue:int;
        #var vectorOfMoves:Vector(Connect4Game);
        bestMoveColumn = 0
        maxValue = 0
        vectorOfMoves = gameBoard.getChildren(this.getSymbol())                                       ######TODO:  child generation
        for moveIndex in vectorOfMoves:
            possMaxValue = this.newAlphaBeta(self, gameBoard, self.masterChief.getAlpha(), self.masterChief.getAlpha(), INITIAL_DEPTH)
        if (maxValue < possMaxValue):
                maxValue = possMaxValue
                bestMoveColumn = vectorOfMoves.elementAt(moveIndex).lastMove
        return bestMoveColumn       

    def newAlphaBeta(self, gameBoard, ply, alpha, beta, Player):
      
        if (isTerminalState(state) != False):
            return gameBoard.evalState(state)                                                         ######TODO:  evalState?
        
        if (ply == 0):
            #minDepth reached, now go deeper in parallel
            return self.parallelAlphaBetaCrazySpectacular(self, gameBoard, EXTENDED_DEPTH, Player)

        if (Player == MAX_PLAYER):
            #instance vars not necessary, but makes method calls cleaner
            currAlpha = self.masterChief.getAlpha()
            currBeta = self.masterChief.getBeta()
            for child in gameBoard.getChildren():                                                    ################ TODO: child generation (and below)
                newVal= self.newAlphaBeta(self, child, ply - 1, currAlpha, currBeta, not Player)
                self.masterChief.setAlpha(max(currAlpha, newVal))
                if (currBeta <= self.masterChief.getAlpha()):
                    break;
             #returning alpha value unnecessary because of master class
        else:
            #instance vars not necessary, but makes method calls cleaner
            currAlpha = self.masterChief.getAlpha()
            currBeta = self.masterChief.getBeta()
            for child in gameBoard.getChildren():                                                    ################ TODO: child generation (and above)
                newVal= self.newAlphaBeta(self, child, ply - 1, currAlpha, currBeta, not Player)
                self.masterChief.setBeta(min(currBeta, newVal))
                if (self.MasterChief.getBeta() <= currAlpha):
                    break
             #returning beta value unnecessary because of master class

    def parallelAlphaBetaCrazySpectacular(self, gameBoard, depth, Player):
        coforall child in gameBoard.getChildren():                                                    ############### TODO:  how is this done in python? 
            #perform regular serial alpha beta on each of these children to Extended_Depth
            alphaBeta(self, child, depth, self.MasterChief.getAlpha(), self.MasterChief.getBeta(), not Player)


    #regular alpha-beta pruning - does not have waiting conditions
    #gameBoard : a state?, ply : int, alpha : int, beta : int, Player : bool
    def alphaBeta(self, gameBoard, ply, alpha, beta, Player):

        #isTerminalState Returns 'x', 'o', False if no one has won, and 'c' for a tie
        if (isTerminalState(state)!=False or ply == 0):
            return (gameBoard.evalState(state))

        if (Player == MAX_PLAYER):
            for child in gameBoard.getChildren():                                                     ####### TODO:  child gen
                newVal= self.alphaBeta(self, child, ply - 1, alpha, beta, not Player)  
                alpha = max(alpha, newVal)
                if (beta <= alpha):
                    break
            return alpha
        else:
            for child in gameBoard.getChildren():                                                     ####### TODO:  child gen
                newVal= self.alphaBeta(self, child, ply -1, alpha, beta, not Player)
                beta = min(beta, newVal)
                if (beta <= alpha):
                    break
            return beta


#############################################

''' MasterChief contains the global alpha and beta vars for a parallel
 * minimax with alpha-beta pruning algorithm
 * 
 * connectChapel
 * python version 1.0
'''

#from multiprocessing import Process, Lock

class MasterChief:
    # sync variables must be blocked from being used.                                           ####TODO:  How do we make sync vars in python?

    def __init__(self):
        #initialize to de facto -/+ infinity
        self.alphaSync = -100000
        self.betaSync = 100000      
      
    '''
     * Get current alpha; Note: sync variable status means it can only be
     * accessed by one thing at a time for read and/or write
     returns int
    '''
    def getAlpha(self):
        return self.alphaSync
    def getBeta(self):
        return self.betaSync
    
    '''
     * Set current Alpha/beta; Note: sync variable status means it can only be
     * accessed by one thing at a time for read and/or write
     (uses ints)
    '''
    def setAlpha(self, newAlpha):
        self.alphaSync = newAlpha
    def setBeta(self, newBeta):
        self.betaSync = newBeta

        
